Version-controlled hooks: skillhook.yaml in a repository, link/unlink, run: shell hooks - #6
Merged
Conversation
…link`, `run:` shell hooks A repository can now declare its webhooks in a skillhook.yaml at its root, checked in with the code they act on. Each hook maps a webhook name to what runs: `run:` (a shell command executed in the repository with the payload on stdin), `skill:` (a SKILL.md directory in the repository, served under the hook's name) or `prompt:` (inline instructions for the agent), plus any field of the `skillhook:` block. Secrets are named in the file and stored per machine in .env. - `skillhook link [dir]` registers a repository in the new `projects` key of skillhook.json; `unlink` removes it; `projects` lists linked repositories with their hooks and URLs; `projects init [dir]` writes a starter file and links it. MCP: list_projects, link_project (with init), unlink_project. - SkillRegistry (now src/registry.ts) serves <home>/skills first, then linked projects in config order, re-reading `projects`, every skillhook.yaml and every referenced SKILL.md when they change, so nothing needs a restart. Duplicate names are reported by skills list/validate, doctor and the server log instead of being served. - A compiled hook is an ordinary Skill with `source.type === "project"`; skills list, skills show, GET /skills, the MCP skill tools and doctor show where a skill comes from. - schema/skillhook.yaml.schema.json is generated next to the config schema, and this repository carries its own skillhook.yaml with a pull-after-merge hook. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A repository can now declare its webhooks in a
skillhook.yamlat its root, version-controlled with the code they act on, so "which skill runs from which webhook" has one reviewable answer. Each hook maps a webhook name to what runs:run:/skill:/prompt:per hook, plus any field of theskillhook:block (auth,when,model,cwd,env,timeout_seconds, …).cwddefaults to the repository; the default secret isSKILLHOOK_SECRET_<HOOK>. Secrets are named in the file, never stored there.skillhook link [dir]registers a repository (newprojectskey inskillhook.json),unlink <dir>removes it,projectslists linked repositories with hooks and URLs,projects init [dir]writes a starter file and links it. MCP:list_projects,link_project(withinit),unlink_project.projects, everyskillhook.yamland every referencedSKILL.mdon change.linkandgit pullneed no restart.~/.skillhook/skillsfirst, then linked repositories in order; a duplicate name is reported byskills list,skills validate,doctorand the server log instead of being served.skills listgained asourcecolumn,skills showasource:line,GET /skillsand the MCP skill tools asourcefield,doctoraproject <dir>check.schema/skillhook.yaml.schema.jsonis generated alongside the config schema (editor completion via theyaml-language-servercomment on the starter file's first line).skillhook.yaml(apull-after-mergehook), the dogfood example of the newdocs/projects.md.Design notes
Skill(source.type === "project"), so the server, queue, runners, prompt builder and job store are untouched. The registry moved tosrc/registry.ts(it needssrc/projects.ts, which needs the schemas insrc/skills.ts; keeping it inskills.tswould have been an import cycle).HookSchemaextendsSkillhookBlockSchema, so new block fields reach hooks automatically.run:is sugar forrunner: shell+shell.command; the existing shell runner is reused unchanged.Security
src/server.tsonly gainsfileandsourcein the skill summary (admin route). No change toauth.ts,runners/env.tsorprompt.ts.skillhook.yamlexactly as one trusts aSKILL.mdin~/.skillhook/skills; only the machine owner can link (config file on disk / admin CLI / MCP), never a webhook.run:commands are argv or/bin/sh -c <literal string from the file>; payload data never reaches a command line (stdin and$SKILLHOOK_PAYLOAD_PATHonly), and the docs say so.secret_env, values stay in.env(mode 600);linkgenerates skillhook-managed secrets exactly likeskills new.Tests
npm run checkis green (124 tests). New:src/projects.test.ts(schema, compilation, path resolution, starter template),src/registry.test.ts(merge order, shadowing, mtime reloads of manifest / SKILL.md / config), an HTTP test that serves arun:hook and askill:hook from a linked repository end to end, and a CLI test forprojects init→link→projects→skills list/show→run→doctor→unlink. CI smoke tests now link this repository's ownskillhook.yamland exerciseprojects initwith the installed tarball.🤖 Generated with Claude Code